/**
 * Copyright (c) 2009-2010 Thales Corporate Services S.A.S.
 * This program and the accompanying materials
 * are made available under the terms of the Eclipse Public License v2.0
 * which accompanies this distribution, and is available at
 * https://www.eclipse.org/legal/epl-v2.0
 *
 * SPDX-License-Identifier: EPL-2.0
 * 
 * Contributors:
 * Thales Corporate Services S.A.S - initial API and implementation
 */
package org.eclipse.egf.model.fcore.provider;

import java.util.ArrayList;
import java.util.Collection;

import org.eclipse.egf.common.helper.EMFHelper;
import org.eclipse.egf.core.fcore.IPlatformFcore;
import org.eclipse.egf.core.fcore.IPlatformFcoreProvider;
import org.eclipse.egf.core.platform.pde.IPlatformBundle;
import org.eclipse.egf.model.edit.EGFModelEditPlugin;
import org.eclipse.egf.model.fcore.FcoreFactory;
import org.eclipse.egf.model.fcore.FcorePackage;
import org.eclipse.egf.model.fcore.commands.resource.FcoreResourceAddCommand;
import org.eclipse.egf.model.fcore.commands.resource.ResourceFeatureAddCommand;
import org.eclipse.egf.model.fcore.commands.resource.ResourceMoveCommand;
import org.eclipse.egf.model.fcore.commands.resource.ResourceRemoveCommand;
import org.eclipse.egf.model.fcore.util.FcoreResourceImpl;
import org.eclipse.emf.common.command.Command;
import org.eclipse.emf.common.command.CompoundCommand;
import org.eclipse.emf.common.command.UnexecutableCommand;
import org.eclipse.emf.common.notify.AdapterFactory;
import org.eclipse.emf.common.notify.Notification;
import org.eclipse.emf.common.util.ResourceLocator;
import org.eclipse.emf.common.util.URI;
import org.eclipse.emf.common.util.UniqueEList;
import org.eclipse.emf.ecore.EClass;
import org.eclipse.emf.ecore.EObject;
import org.eclipse.emf.ecore.EStructuralFeature;
import org.eclipse.emf.ecore.resource.Resource;
import org.eclipse.emf.edit.command.AddCommand;
import org.eclipse.emf.edit.command.CommandParameter;
import org.eclipse.emf.edit.command.CreateChildCommand;
import org.eclipse.emf.edit.command.MoveCommand;
import org.eclipse.emf.edit.command.RemoveCommand;
import org.eclipse.emf.edit.domain.EditingDomain;
import org.eclipse.emf.edit.provider.IChildCreationExtender;
import org.eclipse.emf.edit.provider.ViewerNotification;
import org.eclipse.emf.edit.provider.resource.ResourceItemProvider;

/**
 * @author Xavier Maysonnave
 */
public class FcoreResourceItemProvider extends ResourceItemProvider {

    protected ResourceChildCreationExtenderManager _resourceExtenderManager;

    protected Collection<EClass> _roots;

    /**
     * This constructs an instance from a factory and a notifier.
     */
    public FcoreResourceItemProvider(AdapterFactory adapterFactory, ResourceChildCreationExtenderManager resourceExtenderManager) {
        super(adapterFactory);
        _resourceExtenderManager = resourceExtenderManager;
    }

    public Collection<EClass> getRoots() {
        if (_roots == null) {
            _roots = new UniqueEList<EClass>();
            _roots.add(FcorePackage.Literals.FACTORY_COMPONENT);
            // Add roots defined by extenders
            _roots.addAll(_resourceExtenderManager.getRoots());
        }
        return _roots;
    }

    @Override
    public Collection<?> getNewChildDescriptors(Object object, EditingDomain editingDomain, Object sibling) {
        // Resource
        FcoreResourceImpl resource = (FcoreResourceImpl) object;
        // Build the collection of new child descriptors.
        Collection<Object> newChildDescriptors = new ArrayList<Object>();
        collectNewChildDescriptors(newChildDescriptors, object);
        // Add child descriptors contributed by extenders.
        if (adapterFactory instanceof IChildCreationExtender) {
            newChildDescriptors.addAll(((IChildCreationExtender) adapterFactory).getNewChildDescriptors(object, editingDomain));
        }
        // If a sibling has been specified, add the best index possible to each CommandParameter.
        if (sibling != null) {
            // Unwrap
            sibling = unwrap(sibling);
            // Find the index of the sibling.
            Collection<EObject> children = resource.getContents();
            // For each CommandParameter with a non-null, multi-valued structural feature...
            int i = 0;
            DESCRIPTORS_LOOP: for (Object descriptor : newChildDescriptors) {
                if (descriptor instanceof CommandParameter) {
                    CommandParameter parameter = (CommandParameter) descriptor;
                    // Look for the sibling value or an equivalent in the new child's feature.
                    // If it is found, the child should immediately follow it.
                    i = 0;
                    for (Object innerObject : children) {
                        if (isEquivalentValue(sibling, innerObject)) {
                            parameter.index = i + 1;
                            continue DESCRIPTORS_LOOP;
                        }
                        ++i;
                    }
                }
            }
        }
        return newChildDescriptors;
    }

    /**
     * This adds to <code>newChildDescriptors</code>, a collection of new child
     * descriptors. Typically, {@link org.eclipse.emf.edit.command.CommandParameter}s
     * will be used as descriptors. This implementation adds nothing to the
     * collection, but derived classes should override this method, invoking the
     * superclass implementation and then adding to the collection.
     */
    @Override
    protected void collectNewChildDescriptors(Collection<Object> newChildDescriptors, Object object) {
        newChildDescriptors.add(createChildParameter(FcorePackage.Literals.FACTORY_COMPONENT, FcoreFactory.eINSTANCE.createFactoryComponent()));
    }

    /**
     * This is a convenience method that creates a <code>CommandParameter</code>
     * for a given parent feature and child object.
     */
    @Override
    protected CommandParameter createChildParameter(Object feature, Object child) {
        return new CommandParameter(null, feature, child);
    }

    /**
     * This returns the label text for the adapted class.
     */
    @Override
    public String getText(Object object) {
        Resource resource = (Resource) object;
        StringBuffer buffer = new StringBuffer(resource.getURI() == null ? "" : URI.decode(resource.getURI().toString())); //$NON-NLS-1$
        if (resource instanceof IPlatformFcoreProvider) {
            IPlatformFcore fcore = ((IPlatformFcoreProvider) resource).getIPlatformFcore();
            if (fcore != null) {
                IPlatformBundle platformBundle = fcore.getPlatformBundle();
				if (platformBundle.isTarget()) 
                    buffer.append(" [Target]"); //$NON-NLS-1$
				else if (platformBundle.isRuntime())
					buffer.append(" [Runtime]"); //$NON-NLS-1$
				else 
                    buffer.append(" [Workspace]"); //$NON-NLS-1$
                
                buffer.append(" ["); //$NON-NLS-1$
                buffer.append(platformBundle.getInstallLocation());
                buffer.append("]"); //$NON-NLS-1$
            }
        }
        return buffer.toString();
    }

    /**
     * This returns Fcore.gif.
     */
    @Override
    public Object getImage(Object object) {
        return URI.createURI(EGFModelEditPlugin.INSTANCE.getPluginResourceLocator().getImage("full/obj16/Fcore").toString()); //$NON-NLS-1$
    }

    /**
     * Return the resource locator for this item provider's resources.
     */
    @Override
    public ResourceLocator getResourceLocator() {
        return ((IChildCreationExtender) adapterFactory).getResourceLocator();
    }

    /**
     * This implements delegated command creation for the given object.
     */
    @Override
    public Command createCommand(Object object, EditingDomain domain, Class<? extends Command> commandClass, CommandParameter commandParameter) {
        // Commands should operate on the values, not their wrappers. If the command's values needed to be unwrapped,
        // we'll back get a new CommandParameter.
        CommandParameter oldCommandParameter = commandParameter;
        commandParameter = unwrapCommandValues(commandParameter, commandClass);
        Command result = UnexecutableCommand.INSTANCE;
        if (commandParameter.owner instanceof FcoreResourceImpl) {
            if (commandClass == RemoveCommand.class) {
                result = createRemoveCommand(domain, (FcoreResourceImpl) commandParameter.owner, commandParameter.getCollection());
            } else if (commandClass == AddCommand.class) {
                result = createAddCommand(domain, (FcoreResourceImpl) commandParameter.owner, commandParameter.getCollection(), commandParameter.getIndex());
            } else if (commandClass == MoveCommand.class) {
                result = createMoveCommand(domain, (FcoreResourceImpl) commandParameter.owner, commandParameter.getValue(), commandParameter.getIndex());
            } else if (commandClass == CreateChildCommand.class) {
                CommandParameter newChildParameter = (CommandParameter) commandParameter.getValue();
                result = createCreateChildCommand(domain, (FcoreResourceImpl) commandParameter.owner, newChildParameter.getEStructuralFeature(), newChildParameter.getValue(), newChildParameter.getIndex(), commandParameter.getCollection());
            } else {
                return super.createCommand(object, domain, commandClass, commandParameter);
            }
        } else {
            return super.createCommand(object, domain, commandClass, commandParameter);
        }
        // If necessary, get a command that replaces unwrapped values by their wrappers in the result and affected objects.
        return wrapCommand(result, object, commandClass, commandParameter, oldCommandParameter);
    }

    /**
     * This creates a primitive {@link org.eclipse.emf.edit.command.RemoveCommand}.
     */
    protected Command createRemoveCommand(EditingDomain domain, FcoreResourceImpl resource, Collection<?> collection) {
        CompoundCommand removeCommand = new CompoundCommand(CompoundCommand.MERGE_COMMAND_ALL);
        removeCommand.append(new ResourceRemoveCommand(domain, resource, collection));
        for (Object object : collection) {
            EObject eObject = (EObject) object;
            if (eObject.eContents().isEmpty() == false) {
                removeCommand.append(domain.createCommand(RemoveCommand.class, new CommandParameter(eObject, null, eObject.eContents())));
            }
        }
        return removeCommand;
    }

    /**
     * This creates a primitive {@link org.eclipse.emf.edit.command.AddCommand}.
     */
    protected Command createAddCommand(EditingDomain domain, FcoreResourceImpl resource, Collection<?> collection, int index) {
        Collection<EClass> roots = getRoots();
        if (collection != null) {
            for (Object object : collection) {
                if (object instanceof EObject) {
                    EObject eObject = (EObject) object;
                    if (roots.contains(EMFHelper.solveAgainstStaticPackage(eObject.eClass())) == false) {
                        return UnexecutableCommand.INSTANCE;
                    }
                }
            }
            return new FcoreResourceAddCommand(domain, resource, collection, index);
        }
        return UnexecutableCommand.INSTANCE;
    }

    /**
     * This creates a primitive {@link org.eclipse.emf.edit.command.MoveCommand}.
     */
    protected Command createMoveCommand(EditingDomain domain, FcoreResourceImpl resource, Object value, int index) {
        if (value instanceof EObject) {
            Collection<EClass> roots = getRoots();
            EObject eObject = (EObject) value;
            if (roots.contains(EMFHelper.solveAgainstStaticPackage(eObject.eClass()))) {
                return new ResourceMoveCommand(domain, resource, value, index);
            }
        }
        return UnexecutableCommand.INSTANCE;
    }

    /**
     * This creates a primitive {@link org.eclipse.emf.edit.command.CreateChildCommand}.
     */
    protected Command createCreateChildCommand(EditingDomain domain, FcoreResourceImpl resource, EStructuralFeature feature, Object value, int index, Collection<?> collection) {
        if (collection != null && collection.size() == 1) {
            Object object = collection.iterator().next();
            if (object instanceof EObject) {
                Collection<EClass> roots = getRoots();
                EObject eObject = (EObject) object;
                if (roots.contains(EMFHelper.solveAgainstStaticPackage(eObject.eClass()))) {
                    return new ResourceFeatureAddCommand(domain, resource, feature, value, resource.getContents().indexOf(eObject) == CommandParameter.NO_INDEX ? CommandParameter.NO_INDEX : resource.getContents().indexOf(eObject) + 1);
                }
            }
        }
        return UnexecutableCommand.INSTANCE;
    }

    /**
     * This handles notification by calling {@link #fireNotifyChanged(Notification) fireNotifyChanged}.
     */
    @Override
    public void notifyChanged(Notification notification) {
        switch (notification.getFeatureID(Resource.class)) {
            case Resource.RESOURCE__URI:
                // case Resource.RESOURCE__IS_MODIFIED:
                // case Resource.RESOURCE__IS_TRACKING_MODIFICATION:
                // case Resource.RESOURCE__RESOURCE_SET:
            {
                fireNotifyChanged(new ViewerNotification(notification, notification.getNotifier(), false, true));
                return;
            }
            case Resource.RESOURCE__IS_LOADED:
            case Resource.RESOURCE__CONTENTS: {
                fireNotifyChanged(new ViewerNotification(notification, notification.getNotifier(), true, false));
                return;
            }
        }
        super.notifyChanged(notification);
    }

}
